# -*- coding: utf-8 -*-
# created on 2017/01/05


from mathsolver.functions.base import *
from sympy.abc import x, y
import numpy as np
from sympy import cos, Abs, pi, sqrt, symbols, acos, Mul, Pow, Integer


# 求向量的模长
class XLMoChang(BaseFunction):
    # 两种情况，一种是内存传入的参数{angle:[],'Arith':[],'Abs':[]},一种是xl_mo_compute002传入的参数{angle:[], vector_a: vector_b:}
    def solver(self, *args):
        target = args[0].sympify()
        angle = symbols('angle')
        _abs = self.search('Abs')  # 寻找所有的向量模
        arith = self.search('Arith')  # 寻找所有的向量四则运算
        _input = self.search(angle)
        vector_a, vector_b = _input[0], _input[1]
        theta = pi * _input[2] / 180 if isinstance(_input[2], Integer) else _input[2]
        # 判断参数是哪种情况
        a_value = None
        b_value = None
        if _abs or arith:
            eqs = []
            if _abs:
                for s in _abs:
                    eqs.append(s[0].args[0] - s[1])
            if arith:
                for h in arith:
                    e = h[0] - h[1]
                    e = e.expand()
                    e_new = S.Zero
                    co = e.as_coefficients_dict()
                    # 将所有的变量替换为n*vector(a)*vector(b)的形式，需要把a和b的系数提出来
                    for cc in co:
                        if isinstance(cc, Mul) or isinstance(cc, Pow):  # 如果该变量是vector(a)*vector(b)的格式
                            for c in cc.args:  # 读取每一个vector(a)和vector(b)
                                if str(c.func == 'vector'):
                                    c_new = xl_mo_compute002().vector_expand(c)
                                    e_new = e.subs(c, c_new)
                        else:  # 如果是一个常数
                            e_new = e_new + cc
                    eqs.append(e_new)
            for i in range(len(eqs)):
                eqs[i] = eqs[i].subs(vector_a * vector_b, vector_a * vector_b * cos(theta))
            value_set = solve(eqs, [vector_a, vector_b], set=True)
            value_list = list(value_set[1])
            for v in value_list:
                if v[0] * v[1] >= 0:
                    a_value, b_value = v[0], v[1]
        else:
            a_value = self.search(vector_a)
            b_value = self.search(vector_b)

        if isinstance(target, Abs):
            if str(target.args[0].func) == 'vector':  # 是单独的向量
                res = self.search(target.args[0])
            else:  # 是一个运算后的向量
                eq = target.args[0] ** 2
                eq = eq.expand()
                # co = target.args[0].as_coefficients_dict()
                res = eq.subs(vector_a * vector_b, vector_a * vector_b * cos(theta))
                res = res.subs(vector_a, a_value)
                res = res.subs(vector_b, b_value)
                res = sqrt(res)
        else:
            eq = target.expand()
            res = eq.subs(vector_a * vector_b, vector_a * vector_b * cos(theta))
            res = res.subs(vector_a, a_value)
            res = res.subs(vector_b, b_value)
        self.output.append(BaseValue(res))

        return self


# 已知向量,求未知点坐标
class XLPointCoord(BaseFunction):
    def solver(self, *args):
        _input = args[0].value
        equation = args[1].sympify()
        target_point = args[2].value

        # 定义一个字典存储所有的点及坐标
        points = {}
        for i in _input:
            points[i['name']] = np.array(sympify(i['value']))

        # 定义目标点的坐标
        target_vector = np.array([x, y])

        self.steps.append(["", "设%s点的坐标为(x,y)" % target_point])

        # 定义一个向量组存储两个向量和其系数
        vectors = []
        for e in equation:
            if len(e.args) == 1:
                vectors.append([str(e.args[0]), 1])
            else:
                vectors.append([str(e.args[1].args[0]), e.args[0]])
        vector_1 = None
        vector_2 = None
        # 求两个向量
        for v in vectors:
            if target_point in v[0]:  # 包含目标点
                vector_1 = (points[v[0][1] - target_vector]) * v[1] if target_point == v[0][0] \
                    else (target_vector - points[v[0][0]]) * v[1]
                self.steps.append(["", "则向量%s是%s" % (str(v[0]), str(vector_1))])
            else:  # 包含已知点
                vector_2 = (points[v[0][1]] - points[v[0][0]]) * v[1]
                self.steps.append(["", "已知向量%s是%s" % (str(v[0]), str(vector_2))])

        self.steps.append(["", "又因%s=%s" % (str(equation[0]), str(equation[1]))])

        target_eq = vector_1 - vector_2
        res = solve(target_eq, [x, y])
        res_point = (res[x], res[y])

        self.steps.append(["", "所以%s点的坐标是%s" % (target_point, str(res_point))])
        return self


# 已知向量模长求夹角,都会转换为解包含a,b,theta的方程组
class XLInterAngle(BaseFunction):
    def solver(self, *args):
        vector_a = sympify(args[0].value)
        vector_b = sympify(args[1].value)
        target = args[2].sympify()
        eq = []

        # 将BaseEqs中的参数转换为方程
        for t in target:
            # 如果参数是模长的形式|……|
            if isinstance(t[0], Abs):
                # 参数是一个单独的向量模长|a|
                if str(t[0].args[0].func) == 'vector':
                    e = t[0].args[0] - t[1]
                    eq.append(e.expand())
                # 参数是运算后的向量模长|a+b|
                else:
                    e = t[0].args[0] ** 2 - t[1] ** 2
                    eq.append(e.expand())
            else:
                # 参数是一个单独的向量，但是没有赋值
                if str(t[0].func) == 'vector':
                    pass
                # 参数是一个运算后的向量(a+b)·(a-b)
                else:
                    e = t[0] - t[1]
                    eq.append(e.expand())

        # 定义向量夹角
        theta = symbols('theta')

        self.steps.append(["", "设%s和%s的夹角是%s" % (str(vector_a), str(vector_b), str(theta))])
        self.steps.append(
            ["", "则由题意可得%s·%s=%s" % (str(vector_a), str(vector_b), str(vector_a * vector_b * theta))])
        self.steps.append(["", "根据已知内容可计算得以下方程组:"])

        # 将方程组中的a*b替换为a*b*theta，因为向量点积需要考虑夹角
        for i in range(len(eq)):
            eq[i] = eq[i].subs(vector_a * vector_b, vector_a * vector_b * theta)
            self.steps.append(["", "%s = 0" % str(eq[i])])

        cores = solve(eq, [vector_a, vector_b, theta])[0][2]
        res = acos(cores)

        self.steps.append(["", "∴cos(%s) = %s" % (str(theta), str(cores))])
        self.steps.append(["", "∴%s = %s" % (str(theta), str(res))])

        return self


# 根据向量求投影
class XLProjection(BaseFunction):
    def solver(self, *args):
        pro_a = sympify(args[1].value)
        pro_b = sympify(args[2].value)

        # 判断输入参数的类型
        if isinstance(args[0], BaseVectorEqs):
            # 如果输入的是向量
            # 求得投影向量和被投影向量
            vec1 = np.array(XLScalar().solver(BasePoly(pro_a)).output[0])
            vec2 = np.array(XLScalar().solver(BasePoly(pro_b)).output[0])
        elif isinstance(args[0], BasePoints):
            # 如果输入的是坐标点
            input = args[0].value
            # 定义一个字典存储所有的点及坐标
            points = {}
            for i in input:
                points[i['name']] = np.array(sympify(i['value']))
            vec1 = points[str(pro_a.args[0])[1]] - points[str(pro_a.args[0])[0]]
            vec2 = points[str(pro_b.args[0])[1]] - points[str(pro_b.args[0])[0]]
        else:
            raise ValueError

        # 计算两个向量的点积和被投影向量的模长
        dj = sum(vec1 * vec2)
        vec2_mo = sqrt(sum(vec2 * vec2))

        self.steps.append(["", "∵%s·%s = %s" % (str(pro_a), str(pro_b), str(dj))])
        self.steps.append(["", "又∵|%s| = %s" % (str(pro_b), str(vec2_mo))])

        res = dj / vec2_mo

        self.steps.append(["", "∴%s在%s方向上的投影是%s" % (str(pro_a), str(pro_b), str(res))])

        return self


if __name__ == '__main__':
    print("向量运算")
